﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;


namespace Hotfix
{
    /// <summary>
    /// TCP通道
    /// </summary>
    public class TcpChannel : IDisposable
    {
        #region 字段$属性
        /// <summary>
        /// 是否发送
        /// </summary>
        bool isSending;

        /// <summary>
        /// 是否连接
        /// </summary>
        bool isConnected;

        /// <summary>
        /// 创建一个TCP客户端
        /// </summary>
        TcpClient tcpClient;

        /// <summary>
        /// 封装的TCP类
        /// </summary>
        TCPService tcpService;

        /// <summary>
        /// IP
        /// </summary>
        public IPEndPoint iPEndPoint { get; private set; }

        /// <summary>
        /// 用于发送的缓存区
        /// </summary>
        private readonly RingBuffer sendRingBuffer = new RingBuffer();

        /// <summary>
        /// 用于接收的缓存区
        /// </summary>
        private readonly RingBuffer receRingBuffer = new RingBuffer();

        /// <summary>
        /// 临时缓存区
        /// </summary>
        readonly byte[] recvTempBuffer = new byte[8190];

        /// <summary>
        /// 包解析器
        /// </summary>
        readonly PacketParser packetParser;

        /// <summary>
        /// 控制异步 等待一个任务完成
        /// </summary>
        TaskCompletionSource<Packet> recvTcs;

        private event Action<TcpChannel, SocketError> errorCallback;
        /// <summary>
        /// 错误事件
        /// </summary>
        public event Action<TcpChannel, SocketError> ErrorCallback
        {
            add
            {
                errorCallback += value;
            }
            remove
            {
                errorCallback -= value;
            }
        }



        #endregion

        #region 构造
        /// <summary>
        /// 客户端连接
        /// </summary>
        /// <param name="tcpService">封装的Tcp网络</param>
        /// <param name="tcpClient">Tcp客户端</param>
        /// <param name="iPEndPoint">IP地址</param>
        public TcpChannel(TCPService tcpService, TcpClient tcpClient, IPEndPoint iPEndPoint)
        {
            this.tcpClient = tcpClient;
            this.packetParser = new PacketParser(receRingBuffer);
            this.iPEndPoint = iPEndPoint;
            this.ConnectAsync(iPEndPoint);
        }
        #endregion



        /// <summary>
        /// 异步连接
        /// </summary>
        /// <param name="ip"></param>
        public async void ConnectAsync(IPEndPoint ip)
        {
            try
            {
                await this.tcpClient.ConnectAsync(ip.Address, ip.Port);
                this.isConnected = true;
                this.StartSend();
                this.StartRecv();
            }
            catch (Exception)
            {

                throw;
            }
        }

        public void OnAccepted()
        {
            isConnected = true;
            StartSend();
        }

        /// <summary>
        /// 获取数据
        /// </summary>
        /// <returns></returns>
        public Task<Packet> Recv()
        {
            bool isOk = packetParser.Parser();

            if (isOk)
            {
                Packet packet = packetParser.GetPacket();
                return Task.FromResult(packet);
            }

            recvTcs = new TaskCompletionSource<Packet>();
            return recvTcs.Task;
        }

        /// <summary>
        /// 开始接收数据
        /// </summary>
        private async void StartRecv()
        {
            try
            {
                while (true)
                {
                    int size = receRingBuffer.BufferSectorsSize - receRingBuffer.lastIndex;

                    int length = await tcpClient.GetStream().ReadAsync(this.receRingBuffer.GetLast, this.receRingBuffer.lastIndex, size);

                    if (length == 0)
                    {
                        this.OnError(this, SocketError.NetworkReset);
                        return;
                    }

                    this.receRingBuffer.lastIndex += length;

                    if (this.receRingBuffer.lastIndex == receRingBuffer.BufferSectorsSize)
                    {
                        this.receRingBuffer.AddLast();
                        this.receRingBuffer.lastIndex = 0;
                    }

                    if (recvTcs != null)
                    {
                        bool isOK = this.packetParser.Parser();
                        if (isOK)
                        {
                            Packet packet = this.packetParser.GetPacket();
                            var tcs = this.recvTcs;
                            this.recvTcs = null;
                            tcs.SetResult(packet);
                        }
                    }
                }
            }
            catch (Exception e)
            {

                throw;
            }
        }




        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="listBytes"></param>
        public void Send(List<byte[]> listBytes)
        {
            foreach (var item in listBytes)
            {
                Send(item);
            }
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="bytes">数据</param>
        public void Send(byte[] bytes)
        {
            byte[] size = BitConverter.GetBytes((ushort)bytes.Length);
            sendRingBuffer.SendTo(size);
            sendRingBuffer.SendTo(bytes);
            if (isSending)
                StartSend();
        }



        /// <summary>
        /// 开始发送数据
        /// </summary>
        private async void StartSend()
        {
            try
            {
                if (isSending) return;

                while (true)
                {
                    if (sendRingBuffer.Count == 0)
                    {
                        isSending = false;
                        return;
                    }

                    this.isSending = true;

                    int sendSize = sendRingBuffer.BufferSectorsSize - sendRingBuffer.firstIndex;

                    if (sendSize > sendRingBuffer.Count)
                        sendSize = sendRingBuffer.Count;

                    await this.tcpClient.GetStream().WriteAsync(sendRingBuffer.GetFirst, sendRingBuffer.firstIndex, sendSize);
                    this.sendRingBuffer.firstIndex += sendSize;

                    if (this.sendRingBuffer.firstIndex == this.sendRingBuffer.BufferSectorsSize)
                    {
                        this.sendRingBuffer.RemoveFirst();
                        this.sendRingBuffer.firstIndex = 0;
                    }

                }
            }
            catch (Exception e)
            {
                this.OnError(this, SocketError.SocketError);
                throw;
            }
        }




        public void Dispose()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 错误事件
        /// </summary>
        /// <param name="tcpChannel"></param>
        /// <param name="socketError"></param>
        protected void OnError(TcpChannel tcpChannel, SocketError socketError)
        {
            this.errorCallback?.Invoke(tcpChannel, socketError);
        }
    }
}
